// [[ OnflowGenerator File UUID: fcd767b916904a9a917a95af719204f2  ]]
package com.itdct.onflow.system.file.manager

import com.baomidou.mybatisplus.extension.plugins.pagination.Page
import com.itdct.adexcel.export.bean.ExcelExportResultBean
import com.itdct.adexcel.export.core.ExcelExportAdvance
import com.itdct.adexcel.input.bean.ExcelInputResultBean
import com.itdct.adexcel.input.core.ExcelInputAdvance
import com.itdct.adexcel.input.listener.RowDataListener
import com.itdct.onflow.core.context.ContextHolder
import com.itdct.onflow.core.extend.*
import com.itdct.onflow.core.util.ZipUtil
import com.itdct.onflow.system.common.base.BaseManager
import com.itdct.onflow.system.common.context.Context
import com.itdct.onflow.system.common.websocket.SysWebSocketHandler
import com.itdct.onflow.system.file.constant.SysFileGradeConst
import com.itdct.onflow.system.file.constant.SysFileSourceType
import com.itdct.onflow.system.file.constant.SysTransferCenterExecuteStateConst
import com.itdct.onflow.system.file.constant.SysTransferCenterTransferTypeConst
import com.itdct.onflow.system.file.dto.SysFileTransferToLocalDTO
import com.itdct.onflow.system.file.entity.SysFileEntity
import com.itdct.onflow.system.file.entity.SysFileFailReasonEntity
import com.itdct.onflow.system.file.entity.SysTransferCenterEntity
import com.itdct.onflow.system.file.entity.SysTransferCenterFileRelEntity
import com.itdct.onflow.system.file.mapper.SysFileFailReasonMapper
import com.itdct.onflow.system.file.mapper.SysFileMapper
import com.itdct.onflow.system.file.mapper.SysTransferCenterFileRelMapper
import com.itdct.onflow.system.file.mapper.SysTransferCenterMapper
import org.springframework.stereotype.Component
import org.springframework.web.multipart.MultipartFile
import java.io.File
import java.util.*
import java.util.concurrent.CountDownLatch
import java.util.concurrent.ExecutorService
import java.util.concurrent.atomic.AtomicInteger

/**
 * @author DCTANT
 * @date 2025-09-28 16:32:52
 * @version 1.0
 * @description 系统传输中心中间层
 */
@Component
class SysTransferCenterManager(
    val sysTransferCenterMapper: SysTransferCenterMapper,
    val sysFileManager: SysFileManager,
    val sysFileMapper: SysFileMapper,
    val sysTransferCenterFileRelMapper: SysTransferCenterFileRelMapper,
    val virtualThreadPoolExecutor: ExecutorService,
    val sysFileFailReasonMapper: SysFileFailReasonMapper,
    val sysWebSocketHandler: SysWebSocketHandler
) : BaseManager() {

    /**
     * 传输中心导出
     *  @param context 上下文
     *  @param moduleName 大模块名称，参考系统字典
     *  @param taskName 当前子模块任务名称
     *  @param dataCountPerFile 每个Excel文件的数据量
     *  @param listFunction 查询数据列表方法，建议共用列表查询方法
     *  @param toExportVo 转换为导出vo方法
     */
    fun <ListVo, ExportVo> export(
        context: Context,
        moduleName: String,
        taskName: String,
        dataCountPerFile: Long = 1000L,
        listFunction: () -> List<ListVo>,
        toExportVo: (ListVo) -> ExportVo,
        onSetExport: ((excelInputAdvance: ExcelExportAdvance<ExportVo>) -> Unit)? = null
    ) {
        ContextHolder.setContext(context)

        sendWebsocket(context, "${taskName}开始导出")

        val sysTransferCenterEntity = SysTransferCenterEntity()
        sysTransferCenterEntity.name = taskName
        sysTransferCenterEntity.startTime = Date()
        sysTransferCenterEntity.moduleName = moduleName
        sysTransferCenterEntity.transferType = SysTransferCenterTransferTypeConst.EXPORT
        sysTransferCenterEntity.executeState = SysTransferCenterExecuteStateConst.RUNNING
        sysTransferCenterEntity.platformType = onflowConfig.platformType
        sysTransferCenterMapper.insertEx(sysTransferCenterEntity)

        var totalDataCount = 0L
        var pageNum = 1L
        var fileCount = 0
        context.startPage = true
        var page = Page<ListVo>(pageNum, dataCountPerFile, false)
        context.page = page
        var successCount = 0
        var failCount = 0

        val sysFileEntities = mutableListOf<SysFileEntity>()

        val exportFiles = mutableListOf<File>()
        while (true) {
            try {
                val findList = listFunction()
                if (findList.isEmpty()) {
                    break
                }
                fileCount++
                val relEntity = SysTransferCenterFileRelEntity()
                relEntity.transferCenterId = sysTransferCenterEntity.id!!
                relEntity.errorMessage = null
                relEntity.startTime = Date()

                val excelExportAdvance = ExcelExportAdvance<ExportVo>()
                excelExportAdvance.setShard(false)
                    .setFreezeHead(true)
                    .setWriteHeadRow(true)
                    .setExportPath(onflowConfig.file.localPath)
                    .setExportFileName(taskName + "-" + pageNum)
                onSetExport?.invoke(excelExportAdvance)
                val exportResultBean = excelExportAdvance.export { t ->
                    val exportVos = mutableListOf<ExportVo>()
                    for (listVo in findList) {
                        val exportVo: ExportVo = toExportVo(listVo)
                        exportVos.add(exportVo)
                    }
                    exportVos

                }

                if (exportResultBean.isSuccess) {
                    exportFiles.add(exportResultBean.exportFile)
                    doUploadFile(exportResultBean, moduleName, sysTransferCenterEntity, sysFileEntities, relEntity, findList.size)
                } else {
                    logError(exportResultBean.errorMsg)
                }
                pageNum++
                page = Page<ListVo>(pageNum, dataCountPerFile, false)
                context.page = page
                context.startPage = true
                successCount++
                totalDataCount++
            } catch (e: Exception) {
                logError(e)
                failCount++
            }
        }

        if (totalDataCount > 0) {
            sysTransferCenterEntity.fileCount = fileCount
            sysTransferCenterEntity.successCount = successCount
            sysTransferCenterEntity.failCount = failCount

            if (successCount == 0) {
                sysTransferCenterEntity.executeState = SysTransferCenterExecuteStateConst.FAIL
            } else if (failCount == 0) {
                sysTransferCenterEntity.executeState = SysTransferCenterExecuteStateConst.SUCCESS
            } else {
                sysTransferCenterEntity.executeState = SysTransferCenterExecuteStateConst.NOT_ALL_SUCCESS
            }

            sysTransferCenterEntity.finishTime = Date()

            if (fileCount > 1) {
                // INFO: Zhouwx: 2025/10/9 如果为多文件，则打包为zip，方便下载
                zipAndUpload(taskName, sysTransferCenterEntity, exportFiles, moduleName)
            }
        } else {
            sysTransferCenterEntity.executeState = SysTransferCenterExecuteStateConst.FAIL
            sysTransferCenterEntity.fileCount = 0
            sysTransferCenterEntity.successCount = 0
            sysTransferCenterEntity.failCount = 0
            sysTransferCenterEntity.skipCount = 0
            sysTransferCenterEntity.finishTime = Date()
            sysTransferCenterEntity.errorMessage = "任务执行失败，数据量为0，无法导出"
        }
        sysTransferCenterMapper.updateEx(sysTransferCenterEntity)

        if (context.accountId != null && context.token != null) {
            if (sysTransferCenterEntity.executeState == SysTransferCenterExecuteStateConst.SUCCESS) {
                sendWebsocket(context, "${taskName}导出成功", { map ->
                    if (sysFileEntities.size == 1) {
                        map["fileId"] = sysFileEntities[0].id ?: 0L
                    } else {
                        map["fileId"] = sysTransferCenterEntity.packageDownloadFileId ?: 0L
                    }
                })
            } else {
                sendWebsocket(context, "${taskName}导出失败")
            }
        }

    }

    private fun zipAndUpload(
        taskName: String,
        sysTransferCenterEntity: SysTransferCenterEntity,
        files: MutableList<File>,
        moduleName: String
    ) {
        val zipFilePath = onflowConfig.file.localPath + "/" + taskName + "-" + sysTransferCenterEntity.id + ".zip"
        val zipFile = ZipUtil.packToZip(files, zipFilePath)
        val sysFileEntity = sysFileManager.uploadFile(zipFile, zipFile.name, moduleName, SysFileGradeConst.MEDIUM, SysFileSourceType.USER_UPLOAD, deleteAfterUpload = true)
        sysTransferCenterEntity.packageDownloadUrl = sysFileEntity.storePath
        sysTransferCenterEntity.packageDownloadFileId = sysFileEntity.id
        sysTransferCenterMapper.updateEx(sysTransferCenterEntity)
    }

    fun sendWebsocket(context: Context, message: String, modifySendMap: ((HashMap<String, Any>) -> Unit)? = null) {
        val hashMap = HashMap<String, Any>()
        hashMap["module"] = "transferCenter"
        hashMap["title"] = "传输中心"
        hashMap["message"] = message
        modifySendMap?.invoke(hashMap)
        if (context.accountId != null && context.token != null) {
            sysWebSocketHandler.sendToUser(context.accountId!!, context.token!!, hashMap)
        } else {
            logWarn("accountId or token is null")
        }
    }

    fun <ListVo, ExportVo> asyncExport(
        context: Context,
        moduleName: String,
        taskName: String,
        dataCountPerFile: Long = 1000L,
        listFunction: () -> List<ListVo>,
        toExportVo: (ListVo) -> ExportVo
    ) {
        virtualThreadPoolExecutor.submit {
            export(context, moduleName, taskName, dataCountPerFile, listFunction, toExportVo)
        }
    }

    private fun doUploadFile(
        exportResultBean: ExcelExportResultBean,
        moduleName: String,
        sysTransferCenterEntity: SysTransferCenterEntity,
        sysFileEntities: MutableList<SysFileEntity>,
        relEntity: SysTransferCenterFileRelEntity,
        count: Int
    ) {
        val exportFile = exportResultBean.exportFile

        val sysFileEntity = sysFileManager.uploadFile(
            exportFile,
            exportFile.name,
            moduleName,
            SysFileGradeConst.MEDIUM,
            SysFileSourceType.SYSTEM_UPLOAD,
            null,
            additionalPath = sysTransferCenterEntity.id!!.toString(),
            deleteAfterUpload = false
        )
        sysFileEntities.add(sysFileEntity)

        relEntity.fileId = sysFileEntity.id!!
        relEntity.totalCount = count
        relEntity.successCount = count
        relEntity.failCount = 0
        relEntity.skipCount = 0
        relEntity.executeState = SysTransferCenterExecuteStateConst.SUCCESS
        relEntity.finishTime = Date()
        sysTransferCenterFileRelMapper.insertEx(relEntity)
    }

    /**
     * 多文件导入方法，带传输中心
     * @param context 上下文
     * @param moduleName 模块名称
     * @param taskName 任务名称
     * @param classType 类类型
     * @param rowDataListener 处理导入的每行数据，并判断是否满足导入需求
     * @param onResult 处理每个文件的导入结果
     * @param multipartFiles 导入的文件列表
     * @param fileGrade 文件等级
     * @param onSetInput 设置导入工具相关参数信息
     *
     */
    fun <T> import(
        context: Context,
        moduleName: String,
        taskName: String,
        classType: Class<T>,
        rowDataListener: RowDataListener<T>,
        onResult: (result: ExcelInputResultBean<T>) -> Unit,
        multipartFiles: List<MultipartFile>,
        fileGrade: Int = SysFileGradeConst.LOW,
        onSetInput: ((excelInputAdvance: ExcelInputAdvance<T>) -> Unit)? = null,
    ) {
        if (multipartFiles.isEmpty()) {
            fail("上传文件不能为空")
        }
        sendWebsocket(context, "${taskName}开始导入")
        val dtos = mutableListOf<SysFileTransferToLocalDTO>()
        for (multipartFile in multipartFiles) {
            val dto = sysFileManager.transferToLocal(multipartFile, moduleName)
            dtos.add(dto)
        }
        val transferCenter = SysTransferCenterEntity()
        transferCenter.name = taskName
        transferCenter.transferType = SysTransferCenterTransferTypeConst.IMPORT
        transferCenter.executeState = SysTransferCenterExecuteStateConst.RUNNING
        transferCenter.moduleName = moduleName
        transferCenter.fileCount = dtos.size
        transferCenter.startTime = Date()
        transferCenter.platformType = onflowConfig.platformType
        sysTransferCenterMapper.insertEx(transferCenter)

        doImport(dtos, context, moduleName, fileGrade, transferCenter, onSetInput, rowDataListener, classType, onResult, taskName)
    }

    private fun <T> doImport(
        dtos: MutableList<SysFileTransferToLocalDTO>,
        context: Context,
        moduleName: String,
        fileGrade: Int,
        transferCenter: SysTransferCenterEntity,
        onSetInput: ((excelInputAdvance: ExcelInputAdvance<T>) -> Unit)?,
        rowDataListener: RowDataListener<T>,
        classType: Class<T>,
        onResult: (result: ExcelInputResultBean<T>) -> Unit,
        taskName: String
    ) {
        val countDownLatch = CountDownLatch(dtos.size)
        var successFileCount = AtomicInteger(0)
        var failFileCount = AtomicInteger(0)
        var skipFileCount = AtomicInteger(0)
        var notAllSuccessFileCount = AtomicInteger(0)

        var totalDataCount = AtomicInteger(0)
        for (dto in dtos) {
            virtualThreadPoolExecutor.submit {
                ContextHolder.setContext(context)
                val localFile = dto.localFile ?: fail("文件不存在")
                val originFileName = dto.originFileName
                val sysFileEntity = sysFileManager.uploadFile(localFile, originFileName, moduleName, fileGrade, SysFileSourceType.USER_UPLOAD)

                val relEntity = SysTransferCenterFileRelEntity()
                relEntity.fileId = sysFileEntity.id!!
                relEntity.transferCenterId = transferCenter.id!!
                relEntity.startTime = Date()
                sysTransferCenterFileRelMapper.insertEx(relEntity)

                try {
                    val excelInputAdvance = ExcelInputAdvance<T>()
                    excelInputAdvance.setShard(false)
                    onSetInput?.invoke(excelInputAdvance)

                    excelInputAdvance.setRowDataListener(rowDataListener)

                    val resultBean = excelInputAdvance.input(localFile, classType)
                    onResult(resultBean)

                    val successRowCount = resultBean.successRowCount
                    val failRowCount = resultBean.failRowCount
                    val totalRowCount = resultBean.totalCount
                    val skipRowCount = resultBean.skipRowCount

                    if (totalRowCount == 0L) {
                        relEntity.totalCount = 0
                        relEntity.errorMessage = "该文件内没有数据"
                        relEntity.executeState = SysTransferCenterExecuteStateConst.FAIL
                    } else {
                        totalDataCount.addAndGet(totalRowCount.toInt())

                        // INFO: Zhouwx: 2025/9/30 添加传输中心与单个文件之间的关联关系，并保存对应的结果信息
                        relEntity.totalCount = totalRowCount.toInt()
                        relEntity.successCount = successRowCount.toInt()
                        relEntity.failCount = failRowCount.toInt()
                        relEntity.skipCount = skipRowCount.toInt()
                        if (skipRowCount == totalRowCount) {
                            relEntity.executeState = SysTransferCenterExecuteStateConst.SKIPPED
                            skipFileCount.addAndGet(1)
                        } else if (successRowCount == totalRowCount) {
                            relEntity.executeState = SysTransferCenterExecuteStateConst.SUCCESS
                            successFileCount.addAndGet(1)
                        } else if (failRowCount == totalRowCount) {
                            relEntity.executeState = SysTransferCenterExecuteStateConst.FAIL
                            relEntity.errorMessage = "所有导入的数据均失败了"
                            failFileCount.addAndGet(1)
                        } else {
                            relEntity.executeState = SysTransferCenterExecuteStateConst.NOT_ALL_SUCCESS
                            relEntity.errorMessage = "部分导入的数据失败了"
                            notAllSuccessFileCount.addAndGet(1)
                        }
                    }
                    relEntity.finishTime = Date()
                    sysTransferCenterFileRelMapper.updateEx(relEntity)

                    if (failRowCount > 0) {
                        // INFO: Zhouwx: 2025/9/30 保存错误原因
                        val failRowBeans = resultBean.failRowBeans
                        val failReasonEntities = mutableListOf<SysFileFailReasonEntity>()
                        for (failRowBean in failRowBeans) {
                            val rowIndex = failRowBean.rowIndex
                            for (failReason in failRowBean.failReasons) {
                                val sysFileFailReasonEntity = SysFileFailReasonEntity()
                                sysFileFailReasonEntity.fileId = sysFileEntity.id!!
                                sysFileFailReasonEntity.rowNum = rowIndex
                                sysFileFailReasonEntity.errorMessage = failReason
                                failReasonEntities.add(sysFileFailReasonEntity)
                            }
                        }
                        if (failReasonEntities.isNotEmpty()) {
                            sysFileFailReasonMapper.saveBatch(failReasonEntities)
                        }
                    }

                } catch (e: Exception) {
                    logError(e)
                    failFileCount.addAndGet(1)
                    relEntity.errorMessage = e.message
                    relEntity.executeState = SysTransferCenterExecuteStateConst.FAIL
                    relEntity.finishTime = Date()
                    sysTransferCenterFileRelMapper.updateEx(relEntity)
                } finally {
                    countDownLatch.countDown()
                }
            }
        }
        countDownLatch.await()

        if (totalDataCount.get() == 0) {
            transferCenter.executeState = SysTransferCenterExecuteStateConst.FAIL
            transferCenter.errorMessage = "没有导入的数据"
        } else {
            if (successFileCount.get() == 0) {
                transferCenter.executeState = SysTransferCenterExecuteStateConst.FAIL
            } else if (successFileCount.get() == dtos.size) {
                transferCenter.executeState = SysTransferCenterExecuteStateConst.SUCCESS
            } else {
                transferCenter.executeState = SysTransferCenterExecuteStateConst.NOT_ALL_SUCCESS
            }
        }
        transferCenter.successCount = successFileCount.get()
        transferCenter.failCount = failFileCount.get()
        transferCenter.skipCount = skipFileCount.get()

        transferCenter.finishTime = Date()
        sysTransferCenterMapper.updateEx(transferCenter)
        sendWebsocket(context, "${taskName}导入完成")
    }

    fun <T> asyncImport(
        context: Context,
        moduleName: String,
        taskName: String,
        classType: Class<T>,
        rowDataListener: RowDataListener<T>,
        onResult: (result: ExcelInputResultBean<T>) -> Unit,
        multipartFiles: List<MultipartFile>,
        fileGrade: Int = SysFileGradeConst.LOW,
        onSetInput: ((excelInputAdvance: ExcelInputAdvance<T>) -> Unit)? = null,
    ) {
        if (multipartFiles.isEmpty()) {
            fail("上传文件不能为空")
        }
        sendWebsocket(context, "${taskName}开始导入")
        val dtos = mutableListOf<SysFileTransferToLocalDTO>()
        for (multipartFile in multipartFiles) {
            val dto = sysFileManager.transferToLocal(multipartFile, moduleName)
            dtos.add(dto)
        }
        val transferCenter = SysTransferCenterEntity()
        transferCenter.name = taskName
        transferCenter.transferType = SysTransferCenterTransferTypeConst.IMPORT
        transferCenter.executeState = SysTransferCenterExecuteStateConst.RUNNING
        transferCenter.moduleName = moduleName
        transferCenter.fileCount = dtos.size
        transferCenter.startTime = Date()
        transferCenter.platformType = onflowConfig.platformType
        sysTransferCenterMapper.insertEx(transferCenter)

        virtualThreadPoolExecutor.submit {
            doImport(dtos, context, moduleName, fileGrade, transferCenter, onSetInput, rowDataListener, classType, onResult, taskName)
        }
    }

}